package service

import (
	"errors"
	"fmt"
	"gf-start-kit/app/model/admin"
	"gf-start-kit/app/model/auth_group_access"
	"gf-start-kit/library/constant"
	"gf-start-kit/library/jstr"
	"gf-start-kit/library/resp"
	"gf-start-kit/library/utils"
	"github.com/goflyfox/gtoken/gtoken"
	"github.com/gogf/gf/crypto/gmd5"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/gvalid"
)

var (
	MultiLogin           bool    //是否允许后台管理员多端登陆
	NotCheckAuthAdminIds []int64 //无需验证权限的用户id
	UserError            = errors.New("账号或密码错误")
	AuthError            = errors.New("用户信息验证失败")
	UserFrozenError      = errors.New("用户已被冻结")
	UserLoginError       = errors.New("登录失败")
)

//AdminLogin 后台用户登陆验证
func Login(r *ghttp.Request) (string, interface{}) {
	data := r.GetFormMapStrStr()
	if e := gvalid.CheckMap(data, g.Map{
		"username": "required",
		"password": "required",
	}, g.Map{
		"username": "账号不能为空",
		"password": "密码不能为空",
	}); e != nil {
		resp.Error(r).SetError(e).Json()
	}
	password := utils.EncryptCBC(data["password"], utils.AdminCbcPublicKey)
	fmt.Println(password)
	var keys string
	if MultiLogin {
		keys = data["username"] + password + gmd5.MustEncryptString(r.GetClientIp())
	} else {
		keys = data["username"] + password
	}
	if err, user := signIn(data["username"], password, r); err != nil {
		resp.Error(r).SetError(UserLoginError).Json()
	} else {
		r.SetParam("userInfo", user)
		return keys, user
	}
	return keys, nil
}

// 后台登录返回方法
func LoginAfter(r *ghttp.Request, respData gtoken.Resp) {
	if !respData.Success() {
		resp.Error(r).SetData(respData.Data).SetMsg(respData.Msg).Json()
	} else {
		token := respData.GetString("token")
		userInfo := gconv.Map(r.GetParam("userInfo"))
		resp.Success(r).SetData(g.Map{
			"token":    token,
			"userInfo": userInfo,
		}).Json()
	}
}

//gtoken验证后返回
func AuthAfterFunc(r *ghttp.Request, respData gtoken.Resp) {
	if r.Method == "OPTIONS" || respData.Success() {
		r.Middleware.Next()
	} else {
		resp.Error(r).SetError(AuthError).Json()
	}
}

//后台退出登陆
func LoginOut(r *ghttp.Request) bool {
	//删除在线用户状态
	//authHeader := r.Header.Get("Authorization")
	//if authHeader != "" {
	//	parts := strings.SplitN(authHeader, " ", 2)
	//	if len(parts) == 2 && parts[0] == "Bearer" && parts[1] != "" {
	//		//删除在线用户状态操作
	//		user_online.Model.Delete("token", parts[1])
	//	}
	//}
	//authHeader = r.GetString("token")
	//if authHeader != "" {
	//	//删除在线用户状态操作
	//	user_online.Model.Delete("token", authHeader)
	//}
	return true
}

type LoginInfo struct {
	Id         int64  `json:"id" p:"id"`                 // ID
	Username   string `json:"username" p:"username"`     // 用户名
	Nickname   string `json:"nickname" p:"nickname"`     // 昵称
	Avatar     string `json:"avatar" p:"avatar"`         // 头像
	Permission string `json:"permission" p:"permission"` // 权限
}
type Access struct {
	Id    int64  `json:"id" p:"id"`       // ID
	Rules string `json:"rules" p:"rules"` // 用户名
}

// 用户登录，成功返回用户信息，否则返回nil
func signIn(username, password string, r *ghttp.Request) (error, *LoginInfo) {
	user, err := admin.Model.Where("username=? and password=?", username, password).One()
	if err != nil {
		return err, nil
	}
	if g.IsEmpty(user) {
		return UserError, nil
	}
	access := &Access{}
	//todo::以后可能做多角色合并权限
	err = auth_group_access.Model.
		As("t").
		LeftJoin("ng_auth_group ag", "t.group_id=ag.id").
		Where("t.uid=? and ag.status=0", user.Id).
		Scan(access)
	if !g.IsEmpty(err) {
		return err, nil
	}
	//判断用户状态
	if user.Status == "hidden" {
		return UserFrozenError, nil
	}
	returnData := LoginInfo{
		Id:         user.Id,
		Username:   user.Username,
		Nickname:   user.Nickname,
		Avatar:     user.Avatar,
		Permission: access.Rules,
	}
	//更新登陆时间及ip
	user.Logintime = gtime.Now()
	user.Loginip = r.GetClientIp()
	_, _ = user.Update()
	return nil, &returnData
}

var AdminGfToken *gtoken.GfToken

func InitAdminGfToken() {
	//多端登陆配置
	MultiLogin = g.Cfg().GetBool("gToken.MultiLogin")
	AdminGfToken = &gtoken.GfToken{
		CacheMode:        g.Cfg().GetInt8("gToken.CacheMode"),
		CacheKey:         g.Cfg().GetString("gToken.CacheKey"),
		Timeout:          g.Cfg().GetInt("gToken.Timeout"),
		MaxRefresh:       g.Cfg().GetInt("gToken.MaxRefresh"),
		TokenDelimiter:   g.Cfg().GetString("gToken.TokenDelimiter"),
		EncryptKey:       g.Cfg().GetBytes("gToken.EncryptKey"),
		AuthFailMsg:      g.Cfg().GetString("gToken.AuthFailMsg"),
		MultiLogin:       MultiLogin,
		LoginPath:        getUrl("/sysLogin/login"),
		LoginBeforeFunc:  Login,
		LoginAfterFunc:   LoginAfter,
		LogoutPath:       getUrl("/sysLogin/logout"),
		AuthPaths:        g.SliceStr{getUrl("/system/*")},
		AuthAfterFunc:    AuthAfterFunc,
		LogoutBeforeFunc: LoginOut,
	}
	AdminGfToken.Start()
}
func getUrl(url string) string {
	return jstr.CompatiblePrefixStr(constant.ApiPrefix + url)
}
